﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ContentTypeRealProxy.cs" company="CodePlex">
//   The MIT License
//   Copyright (c) 2013 Julien Blin
//   
//   Permission is hereby granted, free of charge, to any person obtaining a copy
//   of this software and associated documentation files (the "Software"), to deal
//   in the Software without restriction, including without limitation the rights
//   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//   copies of the Software, and to permit persons to whom the Software is
//   furnished to do so, subject to the following conditions:
//   
//   The above copyright notice and this permission notice shall be included in
//   all copies or substantial portions of the Software.
//   
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//   THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace UCodeFirst.Converters
{
    using System;
    using System.Reflection;
    using System.Runtime.Remoting.Messaging;
    using System.Runtime.Remoting.Proxies;

    using UCodeFirst.ContentTypes;

    using Umbraco.Web;

    /// <summary>
    /// The content type real proxy, that allows lazy proxies to be created for <see cref="ContentTypeBase"/>
    /// </summary>
    public class ContentTypeRealProxy : RealProxy
    {
        /// <summary>
        /// The id of the content type.
        /// </summary>
        private readonly int id;

        /// <summary>
        /// The real content, when loaded.
        /// </summary>
        private ContentTypeBase content;

        /// <summary>
        /// Initializes a new instance of the <see cref="ContentTypeRealProxy"/> class.
        /// </summary>
        /// <param name="classToProxy">
        /// The class to proxy.
        /// </param>
        /// <param name="id">
        /// The id.
        /// </param>
        public ContentTypeRealProxy(Type classToProxy, int id)
            : base(classToProxy)
        {
            this.id = id;
        }

        /// <summary>
        /// When overridden in a derived class, invokes the method that is specified in the provided <see cref="T:System.Runtime.Remoting.Messaging.IMessage"/> on the remote object that is represented by the current instance.
        /// </summary>
        /// <returns>
        /// The message returned by the invoked method, containing the return value and any out or ref parameters.
        /// </returns>
        /// <param name="msg">A <see cref="T:System.Runtime.Remoting.Messaging.IMessage"/> that contains a <see cref="T:System.Collections.IDictionary"/> of information about the method call. </param><PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
        public override IMessage Invoke(IMessage msg)
        {
            var methodCallMessage = msg as IMethodCallMessage;
            if (methodCallMessage == null)
            {
                throw new UCodeFirstException(Resources.InternalError);
            }

            // Calls to Id property returns directly.
            if (methodCallMessage.MethodName.Equals("get_Id", StringComparison.InvariantCulture))
            {
                return new ReturnMessage(this.id, methodCallMessage.Args, methodCallMessage.ArgCount, methodCallMessage.LogicalCallContext, methodCallMessage);
            }

            // Otherwise we lazy load the inner content and forward.
            if (this.content == null)
            {
                this.content = new UmbracoHelper(UmbracoContext.Current).TypedContent(this.id).ToStrongContent();
            }

            return this.InvokeMethodCall(methodCallMessage);
        }

        /// <summary>
        /// Forwards calls to the real content.
        /// </summary>
        /// <param name="msg">
        /// The message.
        /// </param>
        /// <returns>
        /// The returned value.
        /// </returns>
        protected virtual IMethodReturnMessage InvokeMethodCall(IMethodCallMessage msg)
        {
            var args = msg.Args;

            try
            {
                var result = msg.MethodBase.Invoke(this.content, args);
                return new ReturnMessage(result, args, msg.ArgCount, msg.LogicalCallContext, msg);
            }
            catch (TargetInvocationException targetEx)
            {
                var realEx = targetEx.InnerException ?? targetEx;
                return new ReturnMessage(realEx, msg);
            }
            catch (Exception ex)
            {
                return new ReturnMessage(ex, msg);
            }
        }
    }
}
